package com.example.dbsample.util.json;

import com.example.dbsample.model.dto.PlatformEnumConfig;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * TODO 整合到：JsonUtils
 */
@Slf4j
public class GsonUtil {
    private static Gson gson = new GsonBuilder().disableHtmlEscaping().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    // 不用创建对象，直接使用Gson，就可以调用方法
    // 当使用GsonBuilder方法是属性为空的时候，输出流的json自付出使用键值key的，显示形式是"key": null，而直接new出来的就没有"key":null的
    private static final Gson GSON = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    /**
     * 无参的私有构造方法
     */
    private GsonUtil() {
    }

    /**
     * 将对象转成json格式
     */
    public static String string(Object object) {
        return GSON.toJson(object);
    }

    public static String stringNotThrow(Object object) {
        if(object == null) {
            return null;
        }

        String result = null;
        try {
            result = GSON.toJson(object);
        } catch (Throwable e) {
            log.info("stringNotThrow error", e);
        }
        return result;
    }

    /**
     * 将json转成特定的cls对象
     */

    public static <T> T toBean(String gsonString, Class<T> cls) {
        return GSON.fromJson(gsonString, cls);
    }

    /**
     * 将json转成特定的cls对象
     */
    public static <T> T toBean(String gsonString, Type type) {
        return GSON.fromJson(gsonString, type);
    }

    /**
     * json字符串转成list
     */
    public static <T> List<T> toList(String gsonString, Type type) {
        return GSON.fromJson(gsonString, type);
    }

    /**
     * json字符串转成list
     */
    public static <T> List<T> toList(String gsonString) {
        Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<List<T>>() {
        }.getType(), new MapTypeAdapter()).create();

        return gson.fromJson(gsonString, new TypeToken<List<T>>() {
        }.getType());
    }

    /**
     * json字符串转成Map
     */
    public static <T> Map<String, T> toMaps(String gsonString) {
        Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<Map<String, T>>() {
        }.getType(), new MapTypeAdapter()).create();

        return gson.fromJson(gsonString, new TypeToken<Map<String, T>>() {
        }.getType());
    }

    public static class MapTypeAdapter extends TypeAdapter<Object> {
        @Override
        public Object read(JsonReader jsonReader) throws IOException {
            JsonToken token = jsonReader.peek();

            switch (token) {
                case BEGIN_ARRAY:
                    List<Object> list = new ArrayList<>();
                    jsonReader.beginArray();
                    while (jsonReader.hasNext()) {
                        list.add(read(jsonReader));
                    }
                    jsonReader.endArray();
                    return list;

                case BEGIN_OBJECT:
                    Map<String, Object> map = new LinkedHashMap<>();
                    jsonReader.beginObject();
                    while (jsonReader.hasNext()) {
                        map.put(jsonReader.nextName(), read(jsonReader));
                    }
                    jsonReader.endObject();
                    return map;

                case STRING:
                    return jsonReader.nextString();

                case NUMBER:
                    /**
                     * 改写数字的处理逻辑，将数字值氛围整型和浮点型
                     */
                    double dbNum = jsonReader.nextDouble();

                    // 数字超过long的最大值，返回浮点类型
                    if (dbNum > Long.MAX_VALUE) {
                        return dbNum;
                    }
                    // 判断数字是否为整数值
                    long lngNum = (long) dbNum;
                    return dbNum;

                case BOOLEAN:
                    return jsonReader.nextBoolean();

                case NULL:
                    jsonReader.nextNull();
                    return null;

                default:
                    throw new IllegalStateException();

            }
        }

        @Override
        public void write(JsonWriter jsonWriter, Object o) throws IOException {
            // 序列化无需实现
        }
    }

    public static  String toJson(Object obj, Type type) {
        return gson.toJson(obj, type);
    }

    public static <T> T fromJson(String str, Class<T> clazz) {
        return gson.fromJson(str, clazz);
    }

    public static <T> T fromJson(Reader reader, Type type) {
        return gson.fromJson(reader, type);
    }

    public static <T> T fromJsonToType(String json, Type type) {
        try {
            return gsonObj().fromJson(json, type);
            //return gson.fromJson(json, type);
        } catch (JsonSyntaxException e) {
            log.warn("from json by type failed");
            throw e;
        }
    }

    private static final ThreadLocal<Gson> THREAD_LOCAL_GSON = new ThreadLocal<Gson>() {
        @Override
        protected Gson initialValue() {
            return new Gson();
        }
    };

    public static final Gson gsonObj() {
        if(null == THREAD_LOCAL_GSON.get()) {
            THREAD_LOCAL_GSON.set(new Gson());
        }
        return THREAD_LOCAL_GSON.get();
    }

    private static JsonParser jsonParser = new JsonParser();

    public static JsonObject parseJsonObject(String str) {
        if(StringUtils.isBlank(str)) {
            return new JsonObject();
        }
        return jsonParser.parse(str).getAsJsonObject();
    }

    public static void main(String[] args) {
        List<PlatformEnumConfig> list = new ArrayList<>();
        PlatformEnumConfig platformEnumConfig1 = new PlatformEnumConfig();
        platformEnumConfig1.setEnumName("PLATFORM_SZ");
        platformEnumConfig1.setCode("shenzhen");
        platformEnumConfig1.setDesc("深圳");

        PlatformEnumConfig platformEnumConfig2 = new PlatformEnumConfig();
        platformEnumConfig2.setEnumName("PLATFORM_CD");
        platformEnumConfig2.setCode("chengdu");
        platformEnumConfig2.setDesc("成都");

        list.add(platformEnumConfig1);
        list.add(platformEnumConfig2);

        System.out.println(GsonUtil.stringNotThrow(list));
    }
}
